package net.uman.framework.modules.orm.hibernate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;



import net.uman.entity.User;
import net.uman.framework.modules.orm.PropertyFilter;
import net.uman.framework.modules.orm.PropertyFilter.MatchType;
import net.uman.framework.modules.orm.PropertyFilter.PropertyType;
import net.uman.framework.modules.search.Condition;
import net.uman.framework.modules.search.Criteria;
import net.uman.framework.modules.search.Order;
import net.uman.framework.modules.utils.ApplicationContextProvider;
import net.uman.framework.modules.utils.ReflectionUtils;
import net.uman.framework.modules.utils.UUIDHexGenerator;
import net.uman.util.Constants;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;

class HQLGenerator {
	private List<PropertyFilter> filters;
	private Criteria criteria;
	private Class entityCls;
	private PropertyFilter.RelationType rel = PropertyFilter.RelationType.AND;
	private String hql;
	private Map<String, Object> values = new HashMap<String, Object>();
	private List<String> tempDataKeys = new ArrayList<String>(); 
	
	private List<String> items;
	
	HQLGenerator(List<String> items, Class cls, Criteria criteria) {
		this.criteria = criteria;
		this.entityCls = cls;
		this.items = items;
	}
	
	HQLGenerator(Class cls, Criteria criteria) {
		this.criteria = criteria;
		this.entityCls = cls;
	}
	
	HQLGenerator(Class cls, List<PropertyFilter> filters) {
		this.filters = filters;
		this.entityCls = cls;
	}
	
	HQLGenerator(List<String> items, Class cls, List<PropertyFilter> filters) {
		this.filters = filters;
		this.entityCls = cls;
		this.items = items;
	}
	
	HQLGenerator(Class cls, List<PropertyFilter> filters, PropertyFilter.RelationType rel) {
		this.filters = filters;
		this.entityCls = cls;
		this.rel = rel;
	}
	
	String getHql() {
		return hql;
	}

	String getDeleteHql() {
		return "delete " + hql;
	}
	
	Map<String, Object> getValues() {
		return values;
	}
	
	void dispose() {
//		TempDataManager tempDataManager = ApplicationContextProvider.getBean(TempDataManager.class); 
//		for (String key : tempDataKeys) {
//			tempDataManager.deleteAll(key);
//		}
//		tempDataKeys.clear();
	}
	
	void process() {
		StringBuffer sb = new StringBuffer();
		if (items != null && items.size() > 0) {
			sb.append("select ");
			boolean first = true;
			for (String s : items) {
				if (first) {
					first = false;
				} else {
					sb.append(",");
				}
				sb.append(s);
			}
			sb.append(" ");
		}
		sb.append("from "+entityCls.getSimpleName());
	
		if (filters != null) {
			boolean firstFilter = true;
			for (PropertyFilter filter : filters) {
				if (firstFilter) {
					sb.append(" where ");
				} else {
					if (rel == PropertyFilter.RelationType.AND) {
						sb.append(" AND ");
					} else {
						sb.append(" OR ");
					}
				}
				firstFilter = false;
				
				if (!filter.isMultiProperty()) { //只有一个属性需要比较的情况.
					sb.append(buildPropertyFilterCriterion(filter.getPropertyName(), filter.getPropertyValue(), filter.getMatchType(), filter.getPropertyType()));
				} else {//包含多个属性需要比较的情况,进行or处理.
					sb.append("(");
					boolean firstParam = true;
					for (String param : filter.getPropertyNames()) {
						if (!firstParam) {
							sb.append(" OR ");
						}
						firstParam = false;
						sb.append(buildPropertyFilterCriterion(param, filter.getPropertyValue(), filter.getMatchType(), filter.getPropertyType()));
					}
					sb.append(")");
				}			
			}
		} else if (criteria != null) {
			String sql = getSQL(criteria);
			if (StringUtils.isNotBlank(sql)) {
				sb.append(" where ").append(sql);
			}
			
			List<String> groups = criteria.getGroups();
			if (groups != null && groups.size() > 0) {
				sb.append(" group by ");
				boolean first = true;
				for (String group : groups) {
					if (first) {
						first = false;
					} else {	
						sb.append(",");
					}
					sb.append(group);
				}
			}

			List<Order> orders = criteria.getOrders();
			if (orders != null && orders.size() > 0) {
				sb.append(" order by ");
				boolean first = true;
				for (Order order : orders) {
					if (first) {
						first = false;
					} else {	
						sb.append(",");
					}
					sb.append(order.getField()).append(order.isAscend() ? " asc" : " desc");
				}
			}
		}
		
		hql = sb.toString();
	}

	private String getSQL(Criteria crit) {
		List<Condition> conds = crit.getConditions();
		if (conds.size() == 1) {
			return buildPropertyFilterCriterion(conds.get(0));
		}
		
		StringBuffer sb = new StringBuffer();
		sb.append("(");
		boolean firstFilter = true;
		for (Condition cond : conds) {
			if (!firstFilter) {
				if (crit.getRelation() == Criteria.AND) {
					sb.append(" AND ");
				} else {
					sb.append(" OR ");
				}
			}
			firstFilter = false;
			
			sb.append(buildPropertyFilterCriterion(cond));
		}
		sb.append(")");
		
		return sb.toString();
	}
	
	protected String buildPropertyFilterCriterion(Condition cond) {
		if (cond.getCriteria() != null) {
			return getSQL(cond.getCriteria());
		}
		
		if (cond.getOperator() == Condition.EQUALS) {
			return _buildPropertyFilterCriterion(cond.getField(), cond.getValue(), MatchType.EQ, cond.isValueField() ? PropertyType.F : null);
		} else if (cond.getOperator() == Condition.NOT_EQUALS) {
			return _buildPropertyFilterCriterion(cond.getField(), cond.getValue(), MatchType.NEQ, cond.isValueField() ? PropertyType.F : null);
		} else if (cond.getOperator() == Condition.LIKE) {
			return _buildPropertyFilterCriterion(cond.getField(), cond.getValue(), MatchType.LIKE, null);
		} else if (cond.getOperator() == Condition.GT) {
			return _buildPropertyFilterCriterion(cond.getField(), cond.getValue(), MatchType.GT, cond.isValueField() ? PropertyType.F : null);
		} else if (cond.getOperator() == Condition.GE) {
			return _buildPropertyFilterCriterion(cond.getField(), cond.getValue(), MatchType.GE, cond.isValueField() ? PropertyType.F : null);
		} else if (cond.getOperator() == Condition.LT) {
			return _buildPropertyFilterCriterion(cond.getField(), cond.getValue(), MatchType.LT, cond.isValueField() ? PropertyType.F : null);
		} else if (cond.getOperator() == Condition.LE) {
			return _buildPropertyFilterCriterion(cond.getField(), cond.getValue(), MatchType.LE, cond.isValueField() ? PropertyType.F : null);
		} else if (cond.getOperator() == Condition.IN || cond.getOperator() == Condition.NOT_IN) {
			Object[] vs = cond.getValues();
			List list = new ArrayList();
			if (vs != null) {
				for (Object o : vs) {
					list.add(o);
				}
			}
			return _buildPropertyFilterCriterion(cond.getField(), list, cond.getOperator() == Condition.IN ? MatchType.IN : MatchType.NIN, null);
		} else {
			throw new IllegalArgumentException("Unknown operation:"+cond.getOperator());
		}
	}
	
	protected String buildPropertyFilterCriterion(final String propertyName, final Object propertyValue, final MatchType matchType, PropertyType propType) {
		StringBuffer sb = new StringBuffer();
		
		if (matchType != MatchType.IN && matchType != MatchType.NIN && propertyValue instanceof List) {
			boolean first = true;
			sb.append("(");
			for (Object o : (List)propertyValue) {
				if (!first) {
					sb.append(" OR ");
				}
				first = false;
				sb.append(_buildPropertyFilterCriterion(propertyName, o, matchType, propType));
			}
			sb.append(")");
			return sb.toString();
		} else {
			return _buildPropertyFilterCriterion(propertyName, propertyValue, matchType, propType);
		}
	}
	
	private String _buildPropertyFilterCriterion(final String propertyName, final Object propertyValue,
			final MatchType matchType, PropertyType propType) {
		Assert.hasText(propertyName, "propertyName不能为空");
		String criterion = null;
		try {
			//根据MatchType构造criterion
			if (MatchType.EQ.equals(matchType)) {
				criterion = buildEQCriterion(propertyName, propertyValue, propType);
			} else if (MatchType.NEQ.equals(matchType)) {
				criterion = buildNEQCriterion(propertyName, propertyValue, propType);
			} else if (MatchType.LIKE.equals(matchType)) {
				criterion = buildLIKECriterion(propertyName,propertyValue);
			} else if (MatchType.LE.equals(matchType)) {
				criterion = propertyName+"<="+convertValue(propertyName, propertyValue, propType);
			} else if (MatchType.LT.equals(matchType)) {
				criterion = propertyName+"<"+convertValue(propertyName, propertyValue, propType);
			} else if (MatchType.GE.equals(matchType)) {
				criterion = propertyName+">="+convertValue(propertyName, propertyValue, propType);
			} else if (MatchType.GT.equals(matchType)) {
				criterion = propertyName+">"+convertValue(propertyName, propertyValue, propType);
			} else if (MatchType.IN.equals(matchType)) {
				criterion = buildInCriterion(propertyName, propertyValue);
			} else if (MatchType.NIN.equals(matchType)) {
				criterion = buildNotInCriterion(propertyName, propertyValue);
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw ReflectionUtils.convertReflectionExceptionToUnchecked(e);
		}
		return criterion;
	}
	
	private String buildNotInCriterion(String propertyName, Object propertyValue) throws IOException {
		String s = buildInCriterion(propertyName, propertyValue);
		return "NOT "+s;
	}
	
	private String buildInCriterion(String propertyName, Object propertyValue) throws IOException {
		StringBuffer sb = new StringBuffer();
		
		if (propertyValue instanceof List) {
			List list = (List)propertyValue;
			if (list.size() == 1) {
				sb.append(buildEQCriterion(propertyName, list.get(0), null));
			} else if (list.size() == 0) {
				sb.append("1 = 2");
			} else if (list.size() <= 20) {
				sb.append(propertyName).append(" IN (").append(convertValue(propertyName, propertyValue, null)).append(")");
			} else {
//				TempDataManager tempDataManager = ApplicationContextProvider.getBean(TempDataManager.class);
//				String key = new UUIDHexGenerator().newUUID();
//				tempDataManager.saveAll(key, list);
//				sb.append(propertyName).append(" IN (SELECT lValue FROM TempData WHERE key=").append(convertValue(propertyName, key, null)).append(")");
//				
//				tempDataKeys.add(key);
			}
		} else {
			sb.append(buildEQCriterion(propertyName, propertyValue, null));
		}
		return sb.toString();
	}
	


	private String buildLikeCriterionByType(String propertyName, Object propertyValue) throws IOException {
		int type = -1;

		
		if (type == -1) {
			if (filters != null) {
				for (PropertyFilter filter : filters) {
					String fn = filter.getPropertyName();
					if (fn.endsWith("payerType")&&propertyName.contains("payerName")) {
						type = Integer.parseInt(filter.getPropertyValue().toString());
						propertyName = propertyName.replace("payerName", "payerId");
						break;
					} else if (fn.endsWith("payeeType") && propertyName.contains("payeeName")) {
						type = Integer.parseInt(filter.getPropertyValue().toString());
						propertyName = propertyName.replace("payeeName", "payeeId");
						break;
					} else if (fn.endsWith("creatorType")&&propertyName.contains("storeName")){
						//用于shoporder的creatorType字段，可能是门店也可能是配送中心，退单
						type = Integer.parseInt(filter.getPropertyValue().toString());
						propertyName = propertyName.replace("storeName", "storeId");
						break;
					}
				}
			} else if (criteria != null) {
				int[] _type = new int[1];
				String pname = getType(criteria, _type, propertyName);
				if (pname != null) {
					propertyName = pname;
					type = _type[0];
				}
			}
		}
		

		
		return null;
	}

	private String getType(Criteria criteria, int[] type, String propertyName) {
		List<Condition> conds = criteria.getConditions();
		if (conds != null) {
			for (Condition cond : conds) {
				if (cond.getCriteria() != null) {
					String pname = getType(cond.getCriteria(), type, propertyName);
					if (pname != null) {
						return pname;
					}
				} else {
					String fn = cond.getField();
					if (fn.endsWith("payerType")&&propertyName.contains("payerName")) {
						type[0] = Integer.parseInt(cond.getValue().toString());
						return propertyName.replace("payerName", "payerId");
					} else if (fn.endsWith("payeeType")&&propertyName.contains("payeeName")) {
						type[0] = Integer.parseInt(cond.getValue().toString());
						return propertyName.replace("payeeName", "payeeId");
					} else if (fn.endsWith("creatorType")&&propertyName.contains("storeName")){
						//用于shoporder的creatorType字段，可能是门店也可能是配送中心，退单
						type[0] = Integer.parseInt(cond.getValue().toString());
						return propertyName.replace("storeName", "storeId");
					}
				}
			}
		}
		
		return null;
	}



	
	private String buildEQCriterion(String propertyName, Object propertyValue, PropertyType propType) {
		// for b2bCode, shopOrderCode, need to convert them to upper case
		if (propertyName.endsWith("Code") && propertyValue instanceof String) {
			propertyValue = ((String)propertyValue).toUpperCase();
		}
		StringBuffer sb = new StringBuffer();
		sb.append(propertyName).append("=").append(convertValue(propertyName, propertyValue, propType));
		return sb.toString();
	}
	
	private String buildNEQCriterion(String propertyName, Object propertyValue, PropertyType propType) {
		StringBuffer sb = new StringBuffer();
		sb.append(propertyName).append("<>").append(convertValue(propertyName, propertyValue, propType));
		return sb.toString();
	}
	
	private String buildLIKECriterion(String propertyName, Object propertyValue) {
		StringBuffer sb = new StringBuffer();
		sb.append(propertyName).append(" LIKE '%").append(propertyValue).append("%'");
		return sb.toString();
	}
	
	private int index = 0;
	private String convertValue(String propertyName, Object propertyValue, PropertyType propType) {
		if (propType == PropertyType.F) {
			return (String)propertyValue;
		} else {
			String name = "P"+index++;
			values.put(name, propertyValue);
			return ":"+name;
		}
	}
	
	public static void main(String[] args) {

	}
}
